#include "parser.h"
#include "errors.h"

#include <set>
#include <map>
#include <list>
#include <iosfwd>
#include <string>
#include <sstream>

namespace CCompiler
{
    void ParserWithSymTable::expectedTokenType(TokenType type, std::string errorMsg)
    {
        if(getTokenType() != type) throw Errors::SyntaxError(errorMsg, tok_);
    }

    void ParserWithSymTable::getVariables(TypesAndNames &lst, SymType *baseType)
    {
        do
        {
            if(getTokenType() == ttComma) nextToken();
            lst.push_back(getSingleNameAndType(baseType, gntmVariables));
        } while (getTokenType() == ttComma);
    }

    void ParserWithSymTable::getNamesAndTypes(TypesAndNames &lst, getNamesAndTypesMode mode)
    {
		SymType *baseType = (SymType *)stack.find(tok_.getText());
        if(!baseType) throw Errors::SyntaxError("Undefined type " + tok_.getText(), tok_);
		if(mode == gntmVariables)
        {
            nextToken();
            getVariables(lst, baseType);
        }
        else
            do
		    {
			    nextToken();
                //if(getTokenType() != ttIdentifier && getTokenType() != ttCloseParenthesis) break;
			    switch(mode)
                {
                case gntmOnlyTypes:
                    if(!baseType)
                    {
                        baseType = (SymType *)stack.find(tok_.getText());
                        nextToken();
                    }
                    lst.push_back(getSingleNameAndType(baseType, gntmOnlyTypes));
                    baseType = NULL;
                    break;
                case gntmFunctionLike:
                    if(!baseType)
                    {
                        baseType = (SymType *)stack.find(tok_.getText());
                        //if(!baseType) throw Errors::SyntaxError("Undefined type " + tok_.getText(), tok_);
                        nextToken();
                    }
                    lst.push_back(getSingleNameAndType(baseType, gntmFunctionLike));
                    if(lst.back().second == "") mode = gntmOnlyTypes;
                    baseType = NULL;
                    break;
                default:
                    break;
                }
		    } while(getTokenType() == ttComma);
	}

    std::string ParserWithSymTable::getName(getNamesAndTypesMode mode)
    {
        if(getTokenType() != ttIdentifier)
        {
            if(mode == gntmVariables) throw Errors::SyntaxError("Expected name declaration", tok_);
            return "";
        }
        else if(mode == gntmOnlyTypes)
            throw Errors::SyntaxError("Identifier unexpected", tok_);
        else
        {
            std::string name = tok_.getText();
            nextToken();
            return name;
        }
    }

    SymType *getExistingTypeOrInsert(SymType *type, SymTableInterface &tbl)
    {
        std::string name = type->getName();
        if(tbl.exists(name))
        {
            delete type;
            return (SymType *)tbl.find(name);
        }
        else
        {
            tbl.getClosestTable().insert(type);
            return type;
        }
    }

    ParserWithSymTable::TypeAndName ParserWithSymTable::getSingleNameAndType(SymType *baseType, getNamesAndTypesMode mode)
    {
        std::string name;
        while(getTokenType() == ttAsterisk) // for pointers
		{
		    baseType = new SymTypePointer(baseType, baseType->getName() + "*");
            baseType = getExistingTypeOrInsert(baseType, stack);
            nextToken();
		}
        if(getTokenType() == ttOpenParenthesis) // pointer to function
        {
            int asteriskCount = 0;
            nextToken();
            while(getTokenType() == ttAsterisk)
            {
                asteriskCount++;
                nextToken();
            }
            name = getName(mode);
            expectedTokenType(ttCloseParenthesis, "Parenthesis Mismatch");
            nextToken();
            if(asteriskCount && getTokenType() == ttOpenParenthesis)
            {
                TypesAndNames lst;
                if(nextToken().getTokenType() != ttCloseParenthesis)
                    getNamesAndTypes(lst, gntmOnlyTypes);
                expectedTokenType(ttCloseParenthesis, "Parenthesis Mismatch");
                nextToken();
                SymFunctionType *ftype = new SymFunctionType(baseType->getType(), "");
                for(size_t i = 0; i < lst.size(); ++i)
                        ftype->push_arg(lst[i].first);
                ftype->rename(ftype->genFuntionTypeName(""));
                ftype = (SymFunctionType *)getExistingTypeOrInsert(ftype, stack);
                std::string asterisks = "*";
                asteriskCount--;
                SymType *fpointer = new SymFunctionPointer(ftype, ftype->genFuntionTypeName("(*)"));
                fpointer = getExistingTypeOrInsert(fpointer, stack);
                while(asteriskCount--)
                {
                    asterisks += '*';
                    fpointer = new SymTypePointer(fpointer, fpointer->getName() + "*");
                    fpointer = getExistingTypeOrInsert(fpointer, stack);
                }
                baseType = fpointer;
            }
            else
            {
                while(asteriskCount--)
                {
                    baseType = new SymTypePointer(baseType, baseType->getName() + "*");
                    baseType = getExistingTypeOrInsert(baseType, stack);
                }
            }
        }
        else
            name = getName(mode);
        while(getTokenType() == ttOpenBracket)
        {
            nextToken();
            expectedTokenType(ttInteger, "Expected integer constant");
            baseType = getExistingTypeOrInsert(new SymTypeArray(baseType, tok_.getIntValue(),
				baseType->getName() + '[' + tok_.getText() + ']'), stack); // don't forget to kill bug
            nextToken();
            expectedTokenType(ttCloseBracket, "Bracket mismatch");
			nextToken();
        }
		return std::make_pair(baseType, name);
	}

    void ParserWithSymTable::parseVariablesOrFunctionDefinition(SymTableInterface &tbl, bool disableFunctions /*= false*/)
    {
        TypesAndNames lst;
        getNamesAndTypes(lst, gntmVariables);
        if(lst.size() == 1 && getTokenType() == ttOpenParenthesis && !disableFunctions) // function
        {
            TypesAndNames argsLst;
            if(nextToken().getTokenType() != ttCloseParenthesis)
                 getNamesAndTypes(argsLst, gntmFunctionLike);
            if(getTokenType() != ttCloseParenthesis) throw Errors::SyntaxError("Parenthesis mismatch", tok_);
            nextToken();
            SymFunctionType * ft = new SymFunctionType(lst.front().first, "");
            for(size_t i = 0; i < argsLst.size(); ++i)
            {
                ft->push_arg(argsLst[i].first);
            }
            ft->rename(ft->genFuntionTypeName(""));
            ft = (SymFunctionType *) getExistingTypeOrInsert(ft, stack);
            SymFunction *f = new SymFunction(ft, lst.front().second);
            if(tbl.exists(f->getName()))
            {
                Symbol *other = tbl.find(f->getName());
                if(other->getSymbolType() != stFunction)
                    throw Errors::SyntaxError("Other symbol with name" + f->getName() + "was previously defined", tok_);
                SymFunction *fother = (SymFunction *) other;
                if(!((*(SymFunctionType *)f->getType()) == *((SymFunctionType *) fother->getType())))
                    throw Errors::SyntaxError("Function declaration differs from previous one", tok_);
                if(!fother->isProto())
                    throw Errors::SyntaxError("Function already has a body", tok_);
                delete f;
                f = fother;
            }
            if(getTokenType() == ttLeftBrace)
            {
                currentf_ = f;
                for(size_t i = 0; i < argsLst.size(); ++i)
                {
                    if(argsLst[i].second == "") argsLst[i].second = f->genAnonName();
                    else if(f->exists(argsLst[i].second))
                        throw Errors::SyntaxError("Argument " + argsLst[i].second + " already exists", tok_);
                    f->insert(new SymVariable(argsLst[i].first, argsLst[i].second));
                }
                tbl.insert(f);
                stack.push(f);
                SynStatementBlock *block = parseStatementBlock();
                stack.pop();
                f->assignBody(block);
            }
            else if(getTokenType() != ttSemicolon) throw Errors::SyntaxError("Missing semicolon", tok_);
            else nextToken();
            tbl.insert(f);
           
        }
        else // variables
            insertIntoTable<SymVariable>(tbl, lst);
    }

    SymTypeStruct *ParserWithSymTable::extractTypeStruct(SymTableInterface &tbl)
    {
        nextToken();
        std::string name = getName(gntmFunctionLike);
        if(name == "") name = tbl.getClosestTable().genAnonName();
        else if(tbl.exists(name))
        {
            Symbol *pretender = tbl.find(name);
            if(pretender->getSymbolType() != stStruct)
                throw Errors::SyntaxError("Name redefinition", tok_);
            if(!((SymTypeStruct *)pretender)->isProto()) throw Errors::SyntaxError("Structure already has an implementation", tok_);
        }
        SymTypeStruct *type = (SymTypeStruct *)getExistingTypeOrInsert(new SymTypeStruct(name), stack);
        if(getTokenType() == ttLeftBrace)
        {
            type->gotImplementation();
            nextToken();
            while(getTokenType() != ttRightBrace)
                parseVariablesOrFunctionDefinition(type->tbl_, true);
            nextToken();
        }
        for(SymTable::iterator i = type->tbl_.begin(); i != type->tbl_.end(); ++i)
            if((*i).second->getSymbolType() == stVariable)
                type->putField((SymVariable *)(*i).second);
        return type;
    }

    void ParserWithSymTable::parseStructures(SymTableInterface &tbl)
    {
        SymTypeStruct *type = extractTypeStruct(tbl);
        if(type->isProto())
        {
            if(getTokenType() != ttSemicolon)
                throw Errors::SyntaxError("Expected semicolon in struct forward declaration", tok_);
        }
        TypesAndNames lst;
        if(getTokenType() != ttSemicolon)
        {
            getVariables(lst, type);
            insertIntoTable<SymVariable>(tbl, lst);
        }
        else
            nextToken();
    }

    void ParserWithSymTable::parseTypedefs(SymTableInterface &tbl)
    {
        nextToken();
        TypesAndNames lst;
        if(getTokenType() == ttStruct)
        {
            SymTypeStruct* type = extractTypeStruct(tbl);
            getVariables(lst, type);
        }
        else if(isExistingType(tok_))
        {
            getNamesAndTypes(lst, gntmVariables);
        }
        else
            throw Errors::SyntaxError("Typedef takes existing type as the first operand", tok_);
        insertIntoTable<SymAlias>(tbl, lst);

    }

    void ParserWithSymTable::parseDeclaration(bool disableFunctions)
    {
        for(;;)
        {
            if(isExistingType(tok_)) // function or variable
                parseVariablesOrFunctionDefinition(stack, disableFunctions);
            else if(getTokenType() == ttTypedef) // alias
                parseTypedefs(stack);
            else if(getTokenType() == ttStruct) // structure
                parseStructures(stack);
            else break;
        }
    }

    SynNode *ParserWithSymTable::oldparse()
    {
        parseDeclaration();
        if(getTokenType() == ttEof)
            throw Errors::EndOfFile();
        SynExpression * res = parseExpression();
        try
        {
            SymType *deb = res->getType(stack);
            deb = deb;
        }
        catch (Errors::SyntaxError e)
        {
            e.changeCoordinates(tok_);
            throw e;
        }
        if(getTokenType() != ttSemicolon) throw Errors::SyntaxError("Missing semicolon", tok_);
        nextToken();
        return res;
    }

	void ParserWithSymTable::parse()
	{
        parseDeclaration();
        if(getTokenType() == ttEof)
            throw Errors::EndOfFile();
        else
            throw Errors::SyntaxError("Expected function, global variable or type declaraion", tok_);
	}

    SynExpression *ParserWithSymTable::internalParseExpression(Int2Type<lowest_level>) // cast-expression
    {
        if(getTokenType() == ttOpenParenthesis)
        {
            Token openParenthesis = tok_;
            if(nextToken().getTokenType() != ttIdentifier || !stack.find(tok_.getText())->isType())
            {
                tokenizer_.putBack(tok_);
                tokenizer_.putBack(openParenthesis);
                nextToken();
                return parseUnaryExpression();
            }
			std::string basetypeName = tok_.getText();
            nextToken();
            ParserWithSymTable::TypeAndName tn = getSingleNameAndType((SymType *)stack.find(basetypeName), gntmOnlyTypes);
            if(tn.second.size())  throw Errors::SyntaxError("Unexpected meet " + tn.second + " in type cast", tok_);
            if(getTokenType() != ttCloseParenthesis)
                throw Errors::SyntaxError("Parenthesis mismatch", tok_);
            nextToken();
            return new SynCast(tn.first, internalParseExpression(Int2Type<lowest_level>()));
        }
        else
            return parseUnaryExpression();
    }

    SynExpression *ParserWithSymTable::parseUnaryExpression()
    {
        if(UnaryOps.count(getTokenType()))
        {
            std::string op = tok_.getText();
            nextToken();
            return new SynUnaryOp(op, internalParseExpression(Int2Type<lowest_level>()));
        }
        else if(getTokenType() == ttSizeof)
        {
            SymType *type;
            if(nextToken().getTokenType() == ttOpenParenthesis)
            {
                Token openParenthesis = tok_;
                if(nextToken().getTokenType() != ttIdentifier || !stack.find(tok_.getText())->isType())
                {
                    tokenizer_.putBack(tok_);
                    tokenizer_.putBack(openParenthesis);
                    SynExpression *expr = parseUnaryExpression();
                    type = expr->getType(stack);
                    delete expr;
                    return new SynSizeof(type);
                }
                std::string basetypeName = tok_.getText();
                nextToken();
                ParserWithSymTable::TypeAndName tn = getSingleNameAndType((SymType *)stack.find(basetypeName), gntmOnlyTypes);
                if(tn.second.size())  throw Errors::SyntaxError("Unexpected meet " + tn.second + " in type cast", tok_);
                if(getTokenType() != ttCloseParenthesis)
                    throw Errors::SyntaxError("Parenthesis mismatch", tok_);
                nextToken();
                type = tn.first;
            }
            else
            {
                SynExpression *expr = parseUnaryExpression();
                type = expr->getType(stack);
                delete expr;
            }
            return new SynSizeof(type);
        }
        else
            return parsePostfixExpression();
    }

    SynExpression *ParserWithSymTable::parsePostfixExpression()
    {
        SynExpression *pe = parsePrimaryExpression();
        for(;;)
        {
            switch(getTokenType())
            {
            case ttOpenParenthesis:
                pe = parseFunction(pe);
                break;
            case ttDot:
                pe = parseStruct(pe);
                break;
			case ttOpenBracket:
                pe = parseArray(pe);
				break;
			case ttArrow:
                pe = parsePointerOnStruct(pe);
				break;
			case ttInc:
				pe = new SynUnaryOp("++", pe, true);
				nextToken();
				break;
            case ttDecr:
                pe = new SynUnaryOp("--", pe, true);
                nextToken();
                break;
            default:
                goto exit;
            }
        }
exit: return pe;
    }

    SynExpression *ParserWithSymTable::parsePrimaryExpression()
    {
        return BasicParser::internalParseExpression(Int2Type<lowest_level>());
    }

	SynExpression *ParserWithSymTable::parseIdentifier()
	{
        std::string name = tok_.getText();
		if(!stack.exists(name)) throw Errors::SyntaxError(name + " name wasn't defined", tok_);
        nextToken();
        return new SynVal(name);
	}

    SynExpression *ParserWithSymTable::parseFunction(SynExpression *expr)
    {
        SymType *type = expr->getType(stack);
        SymFunctionType *ft = NULL;
        switch(type->getSymbolType())
        {
        case stFunctionType:
            ft = (SymFunctionType *)type;
            break;
        case stFunctionPointer:
            ft = (SymFunctionType *)((SymFunctionPointer *)type)->getPointed();
            break;
        default:
            throw Errors::SyntaxError("Expected function or function pointer", tok_);
        }
        SynFunctionCall *funccall = new SynFunctionCall(expr);
        SymFunction::ArgsVector::iterator iter = ft->argsBegin();
        do
        {
            nextToken();
            if(iter != ft->argsEnd())
            {
                SynExpression *param = parseExpression();
                SymType *deb1 = (*(iter++))->getType(), *deb2 = param->getType(stack);
                if(deb1 != deb2)
                    throw Errors::SyntaxError("Arguments type mismatch", tok_);
                funccall->pushArgument(param);
            }
            else if(getTokenType() != ttCloseParenthesis)
                throw Errors::SyntaxError("Arguments count mismatch", tok_);
        } while(getTokenType() == ttComma);
        if(getTokenType() != ttCloseParenthesis)
            throw Errors::SyntaxError("Parenthesis mismatch", tok_);
        nextToken();
        return funccall;
    }

    SynExpression *ParserWithSymTable::parseExpression()
    {
        return parseAssignmentExpression();
    }

    SynExpression *ParserWithSymTable::parseAssignmentExpression()
    {
        SynExpression *res = BasicParser::parseExpression();
        if(AssignOps.count(getTokenType()))
        {
            std::string name = tok_.getText();
            nextToken();
            res = new SynAssignOp(name, res, parseAssignmentExpression());
        }
        return res;
    }

    SynExpression *ParserWithSymTable::parseStruct(SynExpression *expr)
    {
        SymType *type = expr->getType(stack);
        if(type->getSymbolType() != stStruct)
            throw Errors::SyntaxError("Dot operator can be applied only to struct type", tok_);
        SymTypeStruct *tstruct = (SymTypeStruct *)type;
        if(nextToken().getTokenType() != ttIdentifier)
            throw Errors::SyntaxError("Expected identifier", tok_);
        std::string name = tok_.getText();
        if(!tstruct->fieldExists(name))
            throw Errors::SyntaxError(name + " isn't a member of " + tstruct->getName(), tok_);
        nextToken();
        return new SynDot(expr, new SynVal(name));
    }

	SynExpression *ParserWithSymTable::parseArray(SynExpression *expr)
	{
		SymType *artype = expr->getType(stack);
		SymbolType st = artype->getSymbolType();
		if(!(st == stArray || st == stPointer))
			throw Errors::SyntaxError("Indexation can only be applied to arrays or pointers", tok_);
        nextToken();
        SynExpression *index = parseExpression();
        if(index->getType(stack)->getSymbolType() != stInt) throw Errors::SyntaxError("Expected integer expression", tok_);
        if(getTokenType() != ttCloseBracket)
            throw Errors::SyntaxError("Brackets mismatch", tok_);
        nextToken();
        return new SynIndexation(expr, index);
	}


    SynExpression *ParserWithSymTable::parsePointerOnStruct(SynExpression *expr)
    {
        SymType *type = expr->getType(globalTable_);
        if(!(type->getSymbolType() == stPointer &&
            ((SymTypePointer *)type)->getPointed()->getSymbolType() == stStruct))
            throw Errors::SyntaxError("Arrow can be only applied to the pointer on struct", tok_);
        SymTypeStruct *tstruct = (SymTypeStruct *)((SymTypePointer *)type)->getPointed();
        if(nextToken().getTokenType() != ttIdentifier)
            throw Errors::SyntaxError("Expected identifier", tok_);
        std::string fieldname = tok_.getText();
        if(!tstruct->fieldExists(fieldname))
            throw Errors::SyntaxError(fieldname + " isn't a member of " + tstruct->getName(), tok_);
        nextToken();
        return new SynArrow(expr, new SynVal(fieldname));
    }
    SynExpression *ParserWithSymTable::parseCondition()
    {
        SynExpression *condition = parseExpression();
        switch(condition->getType(stack)->getSymbolType())
        {
        case stVoid:
            throw Errors::SyntaxError("Condition can't be a void type", tok_);
        case stStruct:
            throw Errors::SyntaxError("Condition can't be a struct type", tok_);
        default: 
            return condition;
        }     
    }

    SynStatementIf *ParserWithSymTable::parseStatementIf()
    {
        nextToken();
        expectedTokenType(ttOpenParenthesis, "Condition must begin with open parenthesis");
        nextToken();
        SynExpression *condition = parseCondition();
        expectedTokenType(ttCloseParenthesis, "Parenthesis mismatch");
        nextToken();
        SynStatement *action = parseStatement();
        if(getTokenType() == ttElse)
        {
            nextToken();
            SynStatement *elseAction = parseStatement();
            return new SynStatementIfWithElse(condition, action, elseAction);
        }
        return new SynStatementIf(condition, action);
    }

    SynStatementSwitch *ParserWithSymTable::parseStatementSwitch()
    {
        nextToken();
        expectedTokenType(ttOpenParenthesis, "Condition must begin with open parenthesis");
        nextToken();
        SynStatementSwitch *stmt = new SynStatementSwitch(NULL, NULL);
        stmt->assignCondition(parseCondition());
        expectedTokenType(ttCloseParenthesis, "Parenthesis mismatch");
        nextToken();
        stmtContainers.push_back(stmt);
        stmt->assignBody(parseStatement());
        stmtContainers.pop_back();
        return stmt;
    }

    SynStatementReturn *ParserWithSymTable::parseStatementReturn()
    {
         nextToken();
         SynExpression *expr = NULL;
         if(getTokenType() == ttSemicolon)
         {
             if(currentf_->getResultType()->getType()->getSymbolType() != stVoid)
                 throw Errors::SyntaxError("Function returns void", tok_);
             return new SynStatementReturn(expr);
         }
         expr = parseExpression();
         expectedTokenType(ttSemicolon, "expected semicolon");
         SymType *ftype = currentf_->getResultType()->getType() , *etype = expr->getType(stack)->getType();
         if(ftype != etype)
             throw Errors::SyntaxError("Function must return a value with type " + ftype->getName(), tok_);
         return new SynStatementReturn(expr);
    }

    SynStatementFor *ParserWithSymTable::parseStatementFor()
    {
        nextToken();
        expectedTokenType(ttOpenParenthesis, "Expected open parenthesis");
        nextToken();
        SynStatementFor *stmt = new SynStatementFor();
        //SynExpression *init = NULL, *condition = NULL, *iter = NULL;
        if(getTokenType() != ttSemicolon)
        {
            stmt->assignInit(parseExpression());
            expectedTokenType(ttSemicolon, "expected semicolon");
        }
        nextToken();
        if(getTokenType() != ttSemicolon)
        {
            stmt->assignCondition(parseCondition());
            expectedTokenType(ttSemicolon, "expected semicolon");
        }
        nextToken();
        if(getTokenType() != ttCloseParenthesis)
        {
            stmt->assignIter(parseExpression());
            expectedTokenType(ttCloseParenthesis, "Parenthesis mismatch");
        }                                           
        nextToken();
        stmtContainers.push_back(stmt);
        stmt->assignBody(parseStatement());
        stmtContainers.pop_back();
        return stmt;
    }

    SynStatementWhile *ParserWithSymTable::parseStatementWhile()
    {
        nextToken();
        SynStatementWhile *stmt = new SynStatementWhile(NULL, NULL);
        expectedTokenType(ttOpenParenthesis, "Expected open parenthesis");
        nextToken();
        stmt->assignCondition(parseCondition());
        expectedTokenType(ttCloseParenthesis, "Parenthesis mismatch");
        nextToken();
        stmtContainers.push_back(stmt);
        stmt->assignBody(parseStatement());
        stmtContainers.pop_back();
        return stmt;
    }
    SynStatementDoWhile *ParserWithSymTable::parseStatementDoWhile()
    {
        nextToken();
        SynStatementDoWhile *stmt = new SynStatementDoWhile(NULL, NULL);
        stmtContainers.push_back(stmt);
        stmt->assignBody(parseStatement());
        stmtContainers.pop_back();
        expectedTokenType(ttWhile, "expected while");
        nextToken();
        expectedTokenType(ttOpenParenthesis, "expected open parenthesis");
        nextToken();
        stmt->assignCondition(parseCondition());
        expectedTokenType(ttCloseParenthesis, "parenthesis mismatch");
        nextToken();
        expectedTokenType(ttSemicolon, "expected semicolon");
        nextToken();
        return stmt;
    }         

    SynStatementPrintf *ParserWithSymTable::parseStatementPrintf()
    {
        nextToken();
        expectedTokenType(ttOpenParenthesis, "printf operator must be folowed by parameters in parentheises");
        nextToken();
        if(getTokenType() == ttCloseParenthesis)
            throw Errors::SyntaxError("printf doesn't take 0 arguements");
        SynExpression *format = parseExpression();
        if(format->getType(globalTable_)->getName() != "char*")
            throw Errors::SyntaxError("first arguement must be a char* type");
        SynStatementPrintf *stmt = new SynStatementPrintf();
        stmt->add(format);
        while(getTokenType() == ttComma)
        {
            nextToken();
            stmt->add(parseExpression());
        }
        expectedTokenType(ttCloseParenthesis, "parenthesis mismatch");
        nextToken();
        expectedTokenType(ttSemicolon, "expected semicolon");
        nextToken();
        return stmt;
    }

    SynStatement *ParserWithSymTable::parseStatement()
    {
        switch(getTokenType())
        {
        case ttIf:
            return parseStatementIf();
        case ttSwitch:
            return parseStatementSwitch();
        case ttDefault:
            return parseStatementDefault();
        case ttCase:
            return parseStatementCase();
        case ttReturn:
            return parseStatementReturn();
        case ttBreak:
            return parseStatementBreak();
        case ttContinue:
            return parseStatementContinue();
        case ttDo:
            return parseStatementDoWhile();
        case ttWhile:
            return parseStatementWhile();
        case ttFor:
            return parseStatementFor();
        case ttLeftBrace:
            return parseStatementBlock();
        case ttPrintf:
            return parseStatementPrintf();
        default:
            return parseStatementExpression();
        }
    }

    SynStatementDefault *ParserWithSymTable::parseStatementDefault()
    {
        SynStatement *container = defaultAllower();
        if(!container)
            throw Errors::SyntaxError("illegal default", tok_);
        SynStatementSwitch *swContainer = (SynStatementSwitch *) container;
        if(swContainer->haveDefault())
            throw Errors::SyntaxError("more than one default", tok_);
        nextToken();
        expectedTokenType(ttColon, "expected colon");
        nextToken();
        swContainer->setDefault();
        return new SynStatementDefault(parseStatement());
    }

    SynStatementCase *ParserWithSymTable::parseStatementCase()
    {   
        if(!defaultAllower())
            throw Errors::SyntaxError("illegal case", tok_);
        nextToken();
        SynExpression *label = BasicParser::parseExpression();
        expectedTokenType(ttColon, "expected colon");
        nextToken();
        return new SynStatementCase(parseStatement(), label);
    }

    SynStatementBreak *ParserWithSymTable::parseStatementBreak()
    {
        if(!breakAllower())
            throw Errors::SyntaxError("illegal break", tok_);
        nextToken();
        expectedTokenType(ttSemicolon, "Expected semicolon");
        nextToken();
        return new SynStatementBreak();
    }
    SynStatementConinue *ParserWithSymTable::parseStatementContinue()
    {
        if(!continueAllower())
            throw Errors::SyntaxError("illegal continue", tok_);
        nextToken();
        expectedTokenType(ttSemicolon, "Expected semicolon");
        nextToken();
        return new SynStatementConinue();
    }


    SynStatementExpression *ParserWithSymTable::parseStatementExpression()
    {
        if(getTokenType() == ttSemicolon) 
        {
            nextToken();
            return 0;
        }
        SynExpression *expr = parseExpression();
        try
        {
            expr->getType(stack);
        }
        catch (Errors::SyntaxError e)
        {
            e.changeCoordinates(tok_);
            throw e;
        }
        expectedTokenType(ttSemicolon, "Expected semicolon");
        nextToken();
        return new SynStatementExpression(expr);
    }

    SynStatementBlock *ParserWithSymTable::parseStatementBlock()
    {
        SynStatementBlock *block = new SynStatementBlock();
        nextToken();
        stack.push(&block->getSymTable());
        parseDeclaration(true);
        block->localizeVars();
        stmtContainers.push_back(block);
        while(getTokenType() != ttRightBrace)
        {
            SynStatement *stmt = parseStatement();
            if(stmt) block->addStatement(stmt);            
        }
        stmtContainers.pop_back();
        nextToken();
        stack.pop();
        return block;
	}

	bool ParserWithSymTable::isExistingType(Token &tok)
	{
		return isExistingName(tok) && stack.find(tok.getText())->isType();
	}

	bool ParserWithSymTable::isExistingName(Token &tok)
	{
		return tok.getTokenType() == ttIdentifier && stack.exists(tok.getText());
	}

    std::map<BinOpOnTypes, BinOpHandler> AvailableBinOpsAndHandlers;
	std::map<UnaryOpOnType, UnaryOpHandler> AvailableUnaryOpsAndHandlers;

    namespace Handlers
    {
        SymType *UseLeft(SynExpression *&a, SynExpression *&, SymTableInterface &tbl)
        {
            return a->getType(tbl);
        }

        SymType *ReturnIntUnary(SynExpression *&, SymTableInterface &tbl)
        {
            return (SymType *)tbl.find("int");
        }


        SymType *UseLeftWithTypeCheck(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            SymType *at = a->getType(tbl), *bt = b->getType(tbl);
            if(at != bt)
                throw Errors::SyntaxError(at->getName() + " and " + bt->getName() + " are not the same type");
            return a->getType(tbl);
        }

        SymType *ArrayPlusInt(SynExpression *&a, SynExpression *&, SymTableInterface &tbl)
        {
            SymType *t = ((SymTypeArray *)a->getType(tbl))->getPointed();
            return getExistingTypeOrInsert(new SymTypePointer(t, t->getName()+ "*") , tbl);
        }

        SymType *ArrayMinusPtr(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            SymType *at = ArrayPlusInt(a, b, tbl), *bt = b->getType(tbl);
            if(at != bt)
                throw Errors::SyntaxError("'-' can't be applied to pointers with different types (left=" + at->getName() +
                ",right=" + bt->getName() + ')');
            return (SymType*)tbl.find("int");
        }

        SymType *PtrMinusArray(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            SymType *bt = ArrayPlusInt(b, a, tbl), *at = a->getType(tbl);
            if(at != bt)
                throw Errors::SyntaxError("'-' can't be applied to pointers with different types (left=" + at->getName() +
                ",right=" + bt->getName() + ')');
            return (SymType*)tbl.find("int");
        }

        SymType *IntPlusArray(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            return ArrayPlusInt(b, a, tbl);
        }

        SymType *UseLeftWithCast(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            SymType *t = a->getType(tbl);
            b = new SynCast(t, b);
            return t;
        }

        SymType *UseRight(SynExpression *&, SynExpression *&b, SymTableInterface &tbl)
        {
            return b->getType(tbl);
        }

        SymType *UseRightWithCast(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            SymType *t = b->getType(tbl);
            a = new SynCast(t, a);
            return t;
        }

        SymType *AssignFunctionToPtr(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            SymType *at = a->getType(tbl), *bt = b->getType(tbl), *pt = ((SymFunctionPointer *)at)->getPointed();
            if(bt != pt)
                throw Errors::SyntaxError("Can't assign " + bt->getName() + " to " + at->getName());
            return at;
        }

        SymType *DistanceBetweenPointers(SynExpression *&a, SynExpression *&b, SymTableInterface &tbl)
        {
            SymType *at = a->getType(tbl), *bt = b->getType(tbl);
            if(at != bt)
                throw Errors::SyntaxError("'-' can't be applied to pointers with different types (left=" + at->getName() +
                ",right=" + bt->getName() + ')');
            return (SymType*)tbl.find("int");
        }

        SymType *GetFunctionAddress(SynExpression *&a, SymTableInterface &tbl)
        {
            SymFunctionType *t = (SymFunctionType*)a->getType(tbl);
            return getExistingTypeOrInsert(new SymFunctionPointer(t, t->genFuntionTypeName("(*)")), tbl);
        }

        SymType *Dereference(SynExpression *&a, SymTableInterface &tbl)
        {
            return ((SymTypePointer *)a->getType(tbl))->getPointed();
        }

        SymType *LeaveSame(SynExpression *&a, SymTableInterface &tbl)
        {
            return a->getType(tbl);
        }

        SymType *GetAddress(SynExpression *&a, SymTableInterface &tbl)
        {
            SymType *t = a->getType(tbl);
            return getExistingTypeOrInsert(new SymTypePointer(t, t->getName()+ "*") , tbl);
        }
    }

}

namespace __InitializeParserWithSymTable
{
    using namespace CCompiler;

    void put(std::map<BinOpOnTypes, BinOpHandler> &m, TokenType op,
        SymbolType f, SymbolType s, BinOpHandler hndler)
    {
        m[std::make_pair(StringAnalogOfTokenTypeOperation(op), std::make_pair(f, s))] = hndler;
    }

    void put(std::map<UnaryOpOnType, UnaryOpHandler> &m, TokenType op,
        SymbolType f, UnaryOpHandler hndler)
    {
        m[std::make_pair(StringAnalogOfTokenTypeOperation(op), f)] = hndler;
    }

    void initialize()
    {
        // syntax rules for types
        std::map<BinOpOnTypes, BinOpHandler> &bh = AvailableBinOpsAndHandlers;
        using namespace std;
        using namespace Handlers;
        for(int i = 0; i < lowest_level; ++i)
        {
            for(std::set<TokenType>::iterator it = OpGroups[i].begin();
                it != OpGroups[i].end(); ++it)
            {
                put(bh, *it, stInt, stInt, UseLeft);
                put(bh, *it, stChar, stChar, UseLeft);
                put(bh, *it, stInt, stChar, UseLeft);
                put(bh, *it, stChar, stInt, UseRight);
                if(!((i >= 2 && i <=4) || i==7 || *it==ttMod))
                {
                    put(bh, *it, stFloat, stFloat, UseLeft);
                    put(bh, *it, stFloat, stInt, UseLeftWithCast);
                    put(bh, *it, stFloat, stChar, UseLeftWithCast);
                    put(bh, *it, stInt, stFloat, UseRightWithCast);
                    put(bh, *it, stChar, stFloat, UseRightWithCast);
                }
            }
        }
        put(bh, ttPlus, stPointer, stInt, UseLeft);
        put(bh, ttPlus, stPointer, stChar, UseLeft);
        put(bh, ttPlus, stArray, stInt, ArrayPlusInt);
        put(bh, ttPlus, stInt, stArray, IntPlusArray);

        put(bh, ttMinus, stPointer, stInt, UseLeft);
        put(bh, ttMinus, stPointer, stChar, UseLeft);
        put(bh, ttPlus, stInt, stPointer, UseRight);
        put(bh, ttPlus, stChar, stPointer, UseRight);

        put(bh, ttMinus, stPointer, stPointer, DistanceBetweenPointers);
        put(bh, ttMinus, stPointer, stArray, PtrMinusArray);
        put(bh, ttMinus, stArray, stPointer, ArrayMinusPtr);

        put(bh, ttAssign, stInt, stInt, UseLeft);
        put(bh, ttAssign, stChar, stChar, UseLeft);
        put(bh, ttAssign, stFloat, stFloat, UseLeft);
        put(bh, ttAssign, stFloat, stInt, UseLeftWithCast);
        put(bh, ttAssign, stFloat, stChar, UseLeftWithCast);
        put(bh, ttAssign, stInt, stFloat, UseLeftWithCast);
        put(bh, ttAssign, stChar, stFloat, UseLeftWithCast);
        put(bh, ttAssign, stInt, stChar, UseLeft);
        put(bh, ttAssign, stChar, stInt, UseLeft);
        put(bh, ttAssign, stPointer, stPointer, UseLeft);
        put(bh, ttAssign, stPointer, stArray, UseLeft);
        put(bh, ttAssign, stFunctionPointer, stFunctionPointer, UseLeftWithTypeCheck);
        put(bh, ttAssign, stFunctionPointer, stFunctionType, AssignFunctionToPtr);

        put(bh, ttAssign, stStruct, stStruct, UseLeftWithTypeCheck);

        std::map<UnaryOpOnType, UnaryOpHandler> &uh = AvailableUnaryOpsAndHandlers;
        put(uh, ttAsterisk, stPointer, Dereference);
        put(uh, ttAsterisk, stFunctionPointer, Dereference);

        put(uh, ttInc, stInt, LeaveSame);
        put(uh, ttInc, stChar, LeaveSame);
        put(uh, ttInc, stPointer, LeaveSame);
        put(uh, ttDecr, stInt, LeaveSame);
        put(uh, ttDecr, stChar, LeaveSame);
        put(uh, ttDecr, stPointer, LeaveSame);
        put(uh, ttPlus, stInt, LeaveSame);
        put(uh, ttPlus, stChar, LeaveSame);
        put(uh, ttPlus, stFloat, LeaveSame);
        put(uh, ttMinus, stInt, LeaveSame);
        put(uh, ttMinus, stChar, LeaveSame);
        put(uh, ttMinus, stFloat, LeaveSame);
        put(uh, ttNot, stInt, LeaveSame);
        put(uh, ttNot, stChar, ReturnIntUnary);
        put(uh, ttNot, stFloat, ReturnIntUnary);

        put(uh, ttComplement, stInt, LeaveSame);
        put(uh, ttComplement, stChar, LeaveSame);
        put(uh, ttBAnd, stStruct, GetAddress);
        put(uh, ttBAnd, stInt, GetAddress);
        put(uh, ttBAnd, stChar, GetAddress);
        put(uh, ttBAnd, stFloat, GetAddress);
        put(uh, ttBAnd, stPointer, GetAddress);
        put(uh, ttBAnd, stFunctionPointer, GetAddress);
        put(uh, ttBAnd, stFunctionType, GetFunctionAddress);
    }
}
